/*
 * Copyright 2011
 * This file is part of XXXXXX.
 * XXXXXX is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * XXXXXX is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * XXXXXX; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
 * Fifth Floor, Boston, MA 02110-1301 USA
 */
package PD.modules.simulation;

import PD.data.NetworkDataset;
import PD.data.Row;
import PD.data.impl.datasets.SimpleNetworkDataset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

/**
 *
 * @author bicha
 */
public class World {

        private NetworkDataset network;
        private List<Bug> population;
        private Random rand;
        private int maxNumberofChromosomes;
        private int mutationRate;
        private int numberOfBugs;
        private int stoppingCriteria;
        private int numbeOfNodes = 30;

        public World(NetworkDataset network, int maxNumberofChromosomes, int numberOfBugs, int mutationRate, int stoppingCriteria) {
                this.network = network;
                this.maxNumberofChromosomes = maxNumberofChromosomes;
                this.mutationRate = mutationRate;
                this.numberOfBugs = numberOfBugs;
                this.stoppingCriteria = stoppingCriteria;
                this.population = new ArrayList<Bug>();
                this.rand = new Random();

                for (int i = 0; i < numberOfBugs; i++) {
                        Row seed = null;
                        while (seed == null || seed.getWeight() == 0) {
                                seed = network.getNodesRow(rand.nextInt(network.getNumberNodesRows()));
                        }
                        NetworkDataset subNetwork = this.getSubNetwork(seed);
                        if (subNetwork.getNumberNodesRows() > 5) {
                                Bug bug = new Bug(network, subNetwork.clone(), maxNumberofChromosomes, stoppingCriteria);                                
                                this.population.add(bug);
                        }
                }
        }

        public List<Bug> getPopulation() {
                return this.population;
        }

        public void cicle() {
                Comparator<Bug> c = new Comparator<Bug>() {

                        public int compare(Bug o1, Bug o2) {
                                if (o1.getWeight() < o2.getWeight()) {
                                        return 1;
                                } else {
                                        return -1;
                                }
                        }
                };

                //  System.out.println("mutation");
                mutation(mutationRate);
                Collections.sort(population, c);
                // System.out.println("reproduction");
                reproduction();
                // System.out.println("purge");
                // this.removeLonelyNodes();
                Collections.sort(population, c);
                purgue();
                //  System.out.println("remove repeated");
                removeRepeated();
                removeLonelyNodes();
        }


         private void removeLonelyNodes() {
                for (Bug bug : population) {
                        if (bug.getFinalDataset().getNumberNodesRows() < 2 || bug.getFinalDataset().getNumberEdgesRows() < 1) {
                                this.population.remove(bug);
                                this.removeLonelyNodes();
                                return;
                        }
                }
        }

        private void removeRepeated() {
                for (Bug bug : population) {
                        for (Bug bug2 : population) {
                                if (bug != bug2 && bug.isEqual(bug2)) {
                                        this.population.remove(bug);
                                        this.removeRepeated();
                                        return;
                                }
                        }
                }
        }

        private NetworkDataset getSubNetwork(Row seed) {
                List<Row> Nodes = new ArrayList<Row>();
                List<Row> Edges = new ArrayList<Row>();
                Nodes.add(seed.clone());
                constructNetwork(Nodes, Edges, this.numbeOfNodes, seed);
                SimpleNetworkDataset subNetwork = new SimpleNetworkDataset("subNet");
                subNetwork.setEdgesRows(Edges);
                subNetwork.setNodesRows(Nodes);
                return subNetwork;
        }

        private void constructNetwork(List<Row> Nodes, List<Row> Edges, int size, Row seed) {
                List<Row> edges = network.getEdgesfromSourceID(seed.getID());
                for (Row edge : edges) {
                        Row targetNode = network.getNodesRowByID(edge.getTargetVertexID());
                        //it is not taking a subnetwork, it is taking only one path
                        if (Nodes.size() < size && !this.contains(Nodes, targetNode)) {
                                Nodes.add(targetNode.clone());
                                Edges.add(edge.clone());
                                constructNetwork(Nodes, Edges, size, targetNode);
                        }
                }

        }

        private void mutation(int mutationRate) {
                for (int i = 0; i < mutationRate; i++) {
                        Row seed = null;
                        while (seed == null || seed.getWeight() == 0) {
                                seed = network.getNodesRow(rand.nextInt(network.getNumberNodesRows()));
                        }
                        NetworkDataset subNetwork = this.getSubNetwork(seed);
                        if (subNetwork.getNumberNodesRows() > 5) {
                                Bug bug = new Bug(network, subNetwork.clone(), maxNumberofChromosomes, stoppingCriteria);
                                this.population.add(bug);
                        }
                }
        }

        private void reproduction() {
                List<Bug> childs = new ArrayList<Bug>();
                for (int i = 0; i < population.size(); i++) {
                        if (getProbability(i)) {
                                Bug mother = this.population.get(i);
                                for (int j = 0; j < population.size(); j++) {
                                        Bug father = this.population.get(j);
                                        if (mother != father && getProbability(j)) {
                                                Row motherSeed = mother.isConected(father);
                                                if (motherSeed != null) {
                                                        childs.add(new Bug(mother, father, this.numbeOfNodes, motherSeed, this.maxNumberofChromosomes, this.stoppingCriteria));
                                                       // break;
                                                }
                                        }
                                }
                        }
                }

                for (Bug child : childs) {
                        this.population.add(child);
                }
        }

        private boolean getProbability(int i) {
                double p = (double) i / this.population.size();
                double r = this.rand.nextDouble();
                if (r > p) {
                        return true;
                }
                return false;
        }

        private void purgue() {
                List<Bug> death = new ArrayList<Bug>();
                for (int i = this.numberOfBugs; i < this.population.size(); i++) {
                        death.add(this.population.get(i));
                }
                for (Bug b : death) {
                        this.population.remove(b);
                }
        }      

        private boolean contains(List<Row> Nodes, Row targetNode) {
                for (Row node : Nodes) {
                        if (node.getID() == targetNode.getID()) {
                                return true;
                        }
                }
                return false;
        }
}
